/**

 * @author admin

 * 2014骞?鏈?鏃?

 */

package com.tmall.testgen.client.runinspring.push.jmx;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.tmall.testgen.client.runinspring.push.web.TestGenMockServletConfig;
import com.tmall.testgen.client.runinspring.push.web.TestGenMockServletContext;
import com.tmall.testgen.client.runinspring.push.web.WebXmlInfo;

public class SpringController {
	private static  Object applicationContext;
	private  static List<Object> servletContextListeners=new ArrayList<Object>();
	private static Object servletContextEventObj;
	private static XStream xstream = new XStream(new DomDriver  ());
	
	public static  void initApplicationContext(String configPath) throws Exception{
		
		if(configPath.endsWith("web.xml")){
			try{
				applicationContext= getApplicationContextFromWebXml(configPath);
			}catch(Exception e){
				throw e;
			}
		}else{
		    if(!configPath.startsWith("file:"))
		        configPath="file:"+configPath;
		
			Class appContenxtClass=Class.forName("org.springframework.context.support.FileSystemXmlApplicationContext");
			Constructor appContenxtCt=appContenxtClass.getConstructor(String.class);
			applicationContext= appContenxtCt.newInstance(configPath);
		}
	}
	
	public static List<DBInfoForJmx> getDbInfo(){
		return  ConfigExtractor.getDB(applicationContext);
	}

	public static ComputeResult getComputeResult(String clazz,String methodName,Class[] paramTypes ,Object[] params)throws Exception{
		
		Map<String,?> beans=getBeanByClass(clazz);
		String beanName=beans.keySet().iterator().next();
		Object bean= beans.get(beanName);
		
		ComputeResult result=new ComputeResult();
		result.setBeanName(beanName);
		
		Class [] paramClazz=null;
		//如果是基本类型，则使用paramTypes
		if(paramTypes!=null){
			paramClazz=new Class[paramTypes.length];
			for(int i=0;i<paramTypes.length;i++){
			if(params[i]==null||isBasicType(params[i].getClass()))
				paramClazz[i]=paramTypes[i];
			else
				paramClazz[i]=Class.forName(paramTypes[i].getName());
			}
		}
		
		
		Method method=bean.getClass().getMethod(methodName, paramClazz);
		result.setResult(xstream.toXML(method.invoke(bean, params)));
		
		return result;
	}
	
	public static void clearContext(){
		try{
					
			for(Object servletContextListenerObj:servletContextListeners){
				Method lm=null;
				try{
					lm=servletContextListenerObj.getClass().getDeclaredMethod("contextDestroyed", Class.forName("javax.servlet.ServletContextEvent"));
				}catch(Exception e){
					try{
					lm=servletContextListenerObj.getClass().getMethod("contextDestroyed", Class.forName("javax.servlet.ServletContextEvent"));
					}catch(Exception e2){
						
					}
				}
				if(lm==null)
					continue;
				
				try {
					lm.invoke(servletContextListenerObj, servletContextEventObj);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			Method closApplicationContextMethod=applicationContext.getClass().getMethod("destroy");
			closApplicationContextMethod.invoke(applicationContext);
			
			servletContextListeners=new ArrayList<Object>();
			servletContextEventObj=null;
		
	
		}catch(Exception e ){
			
		}
	}
	
	
	private static  Map<String,?> getBeanByClass(String beanClass)throws Exception{
		Map<String,?> beans= (Map<String, ?>) applicationContext.getClass().getMethod("getBeansOfType",Class.class).invoke(applicationContext,Class.forName(beanClass)) ;
		if(beans.size()==0||beans.size()>1){
			Exception e= new Exception("对应的类没有在Spring中注册或存在存在多个注册类型匹配");
			throw e; 
		}
		return beans;
		
	}
	/**
	 * 对函数格式化处理
	 */
	private static String formatMethod(String str){
		str=str.trim();
		while(str.indexOf("  ")>0){
			str=str.replaceAll("  ", " ");
		}
		while(str.indexOf(" (")>0){
			str=str.replaceAll(" \\(", "(");
		}
		while(str.indexOf("( ")>0){
			str=str.replaceAll("\\( ", "(");
		}
		while(str.indexOf(" )")>0){
			str=str.replaceAll(" \\)", ")");
		}
		while(str.indexOf(") ")>0){
			str=str.replaceAll("\\) ", ")");
		}
		while(str.indexOf(" ,")>0){
			str=str.replaceAll(" ,", ",");
		}
		while(str.indexOf(", ")>0){
			str=str.replaceAll(", ", ",");
		}
		
		return str;
	}
	
	
	

	/**
	 * @param targetProjectWebXMLPath
	 * @param classLoader
	 * @return
	 * @throws Exception
	 */
	private static  Object getApplicationContextFromWebXml(String targetProjectWebXMLPath) throws Exception{
		WebXmlInfo webXmlInfo=new WebXmlInfo();
		webXmlInfo.initWebXmlInfo(targetProjectWebXMLPath);
		
		Object testGenMockServletContextObj=Class.forName(TestGenMockServletContext.class.getName()).newInstance();
		Method m=testGenMockServletContextObj.getClass().getDeclaredMethod("setTargetWebAppPath", String.class);
		m.invoke(testGenMockServletContextObj, targetProjectWebXMLPath.toLowerCase().replaceAll("/WEB-INF/web.xml".toLowerCase(), ""));
		
		Method addInitParamM=testGenMockServletContextObj.getClass().getDeclaredMethod("addInitParam", String.class,String.class);
		for(String key:webXmlInfo.getInitParam().keySet()){
			addInitParamM.invoke(testGenMockServletContextObj,key, webXmlInfo.getInitParam().get(key));
		}
		
		Constructor servletContextEventCt=Class.forName("javax.servlet.ServletContextEvent").getDeclaredConstructor(Class.forName("javax.servlet.ServletContext"));
		servletContextEventObj= servletContextEventCt.newInstance(testGenMockServletContextObj);
		

		for(String listener:webXmlInfo.getListeners()){
			
			Object servletContextListenerObj =  Class.forName(listener).newInstance();
				Method lm=null;
			try{
				lm=servletContextListenerObj.getClass().getDeclaredMethod("contextInitialized", Class.forName("javax.servlet.ServletContextEvent"));
			}catch(Exception e){
				lm=servletContextListenerObj.getClass().getMethod("contextInitialized", Class.forName("javax.servlet.ServletContextEvent"));
			}
			if(lm==null)
				continue;
			
			servletContextListeners.add(servletContextListenerObj);
			try{
			lm.invoke(servletContextListenerObj, servletContextEventObj);
			}catch(Exception e){
				e.printStackTrace();
			}
			//scl.contextInitialized(event);
			
		}
		

		
		Class ownerClass = Class.forName("org.springframework.web.context.ContextLoader");
		Method getCurrentWebApplicationContextMethod=ownerClass.getMethod("getCurrentWebApplicationContext");
		Object result=getCurrentWebApplicationContextMethod.invoke(ownerClass);
		
		if(result==null&&webXmlInfo.getDispatcherServletClass()!=null){
			Class dispatcherClass=Class.forName(webXmlInfo.getDispatcherServletClass());
			Object  dispatcherObj=dispatcherClass.newInstance();
			
			Object servletConfig=Class.forName(TestGenMockServletConfig.class.getName()).newInstance();
			Method setContextMethod=Class.forName(TestGenMockServletConfig.class.getName()).getDeclaredMethod("setServletContext", Class.forName("javax.servlet.ServletContext"));
			setContextMethod.invoke(servletConfig, testGenMockServletContextObj);
			
			
			
			Method initMethod=Class.forName("javax.servlet.GenericServlet").getDeclaredMethod("init", Class.forName("javax.servlet.ServletConfig"));
			initMethod.invoke(dispatcherObj, servletConfig);
			
			Method getWebApplicationContextMethod=Class.forName("org.springframework.web.servlet.FrameworkServlet").getDeclaredMethod("getWebApplicationContext") ; 
			result=getWebApplicationContextMethod.invoke(dispatcherObj);
		}
		return result;
		
		
	}
	
	private static boolean isBasicType(Class clazz){
		if(clazz==null)
			return true;
		
		if( String.class.isAssignableFrom(clazz) )
			return true;
		
		if(Boolean.class.isAssignableFrom(clazz) ||clazz.getName().equals("boolean"))
			return true;
		if(Byte.class.isAssignableFrom(clazz) ||clazz.getName().equals("byte") )
			return true;
		if(Short.class.isAssignableFrom(clazz) ||clazz.getName().equals("short"))
			return true;
		if(Integer.class.isAssignableFrom(clazz)||clazz.getName().equals("int") )
			return true;
		if(Long.class.isAssignableFrom(clazz)||clazz.getName().equals("long"))
			return true;
		if(Float.class.isAssignableFrom(clazz) ||clazz.getName().equals("float"))
			return true;
		if(Double.class.isAssignableFrom(clazz) ||clazz.getName().equals("double"))
			return true;
		
		
		
		return false;
		
	}

}
